package ch.zhaw.skuh.turing.controller;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import ch.zhaw.skuh.turing.model.turingmaschine.NG_Factorial_N_ary;
import ch.zhaw.skuh.turing.model.turingmaschine.NG_Multiplication_N_ary;
import ch.zhaw.skuh.turing.model.turingmaschine.Turing;
import ch.zhaw.skuh.turing.model.turingmaschine.TuringChar;
import ch.zhaw.skuh.turing.view.GUI;

public class Controller {

	public enum MODE {
		GUI, CONSOLE
	}

	// private Turing turing;
	private List<Turing> allTuring;
	private Turing turing;

	private GUI gui;

	public Controller(MODE mode, int index, String file, String input) {

		if (mode == MODE.GUI) {
			loadGUI();
		} else {
			loadConsole(index, file, input);
		}
	}

	private void loadConsole(int index, String file, String input) {

		System.out.println("loading Turing Maschine ...");

		switch (index) {
		case 0:
			turing = new NG_Multiplication_N_ary(2);
			break;
		case 1:
			turing = new NG_Factorial_N_ary(2);
			break;
		case 2:
			turing = new NG_Multiplication_N_ary(3);
			break;
		case 3:
			turing = new NG_Factorial_N_ary(3);
			break;
		case 4:
			turing = new NG_Multiplication_N_ary(10);
			break;
		case 5:
			turing = new NG_Factorial_N_ary(10);
			break;
		case 6:
			turing = new NG_Factorial_N_ary(16);
			break;
		default:
			turing = new NG_Factorial_N_ary(10);
			break;
		}

		System.out.println(turing.getName());
		System.out.println("Input = " + input);
		System.out.println("loading rule-set ...");

		// TODO: unsafe-casting (unclean implementation imho)
		TuringChar turingChar = (TuringChar) turing;
		// fill with input
		turingChar.setTapes(input);

		// calc
		System.out.println("start calculation ...");
		long time = System.nanoTime();
		turing.getMaschine().run();
		time = System.nanoTime() - time;

		try {
			LogWriter.writeLog(new File(file), turingChar, time);
		} catch (FileNotFoundException fnfe) {
			System.out.println("Invalid file:" + file);
		} catch (IOException ioe) {
			System.out.println("can't write to file:" + file);
		}
	}

	private void loadGUI() {
		allTuring = new ArrayList<Turing>();
		// allTuring.add(new Addition());
		// allTuring.add(new AddSkript());
		// allTuring.add(new AddBinary());
		// allTuring.add(new MultiUnary());
		// allTuring.add(new MultiBinary());
		// allTuring.add(new FactorialBinary());
		// allTuring.add(new Addition_N_ary(3));
		// allTuring.add(new Subtraction_N_ary(3));
		// allTuring.add(new Multiplication_N_ary(3));
		// allTuring.add(new Factorial_N_ary(3));
		allTuring.add(new NG_Multiplication_N_ary(2));
		allTuring.add(new NG_Factorial_N_ary(2));
		allTuring.add(new NG_Multiplication_N_ary(3));
		allTuring.add(new NG_Factorial_N_ary(3));
		allTuring.add(new NG_Multiplication_N_ary(10));
		allTuring.add(new NG_Factorial_N_ary(10));
		allTuring.add(new NG_Factorial_N_ary(16));

		gui = new GUI(this);
		gui.setVisible();
	}

	public void initOperation(Turing turing, String input) {
		this.turing = turing;
		if (turing.isOutputEnabled()) {
			// TODO: unsafe-casting (unclean implementation imho)
			TuringChar turingChar = (TuringChar) turing;
			// fill with input
			turingChar.setTapes(input);

			gui.setOutputPanel(turingChar, 0);
			showState(0);
		}
	}

	private void showState(long time) {
		// TODO: unsafe-casting (unclean implementation imho)
		TuringChar turingChar = (TuringChar) turing;

		gui.setOutputPanel(turingChar, time);

		// enable/disable buttons
		gui.getControlPanel().enableRun(turingChar.getMaschine().hasRule());

		gui.setVisible();
	}

	// getter/setter
	public ArrayList<TuringChar> getDisplayTuring() {
		ArrayList<TuringChar> displayTuring = new ArrayList<TuringChar>();
		for (Turing t : allTuring) {
			// TODO: better implementation: currently checking a static var
			if (t.isOutputEnabled()) {
				displayTuring.add((TuringChar) t);
			}
		}
		return displayTuring;
	}

	private void doStep(int count) {
		if (count == 0) {
			turing.getMaschine().run();
		} else {
			for (int i = 0; count == 0 || i < count; i++) {
				if (!turing.doStep()) {
					break;
				}
			}
		}
	}

	public void showStep(int count) {
		long time = System.nanoTime();
		doStep(count);
		time = System.nanoTime() - time;
		showState(time);
	}

	/*
	 * 
	 * public List<Turing> getAllOperator() { return allTuring; }
	 */
}
